clear all
set more off, perm
set maxvar 120000
set varabbrev off
* ---------------------------------------------------------- *
global dir 	"`1'"
global Data 	$dir/Data
global Tables 	$dir/Tables
global Figures 	$dir/Figures
global Work 	$dir/Work
global Temp 	$dir/Temp
global Pseudo	$dir/Work/Temp
* ---------------------------------------------------------- *
* Compute returns between IBES periods using daily CRSP data *
* ---------------------------------------------------------- *
* Adjust CRSP's delisting returns following Shumway
import sas $Data/CRSP/msedelist.sas7bdat, clear case(lower)
keep permco permno dlstdt dlstcd dlret dlretx dlpdt dlprc dlamt 

* Shumway adjustment for financially-distressed firms with missing delisting returns:
replace dlret =-0.3 if missing(dlret)  & dlstcd>=400 & dlstcd<600
replace dlretx=-0.3 if missing(dlret)  & dlstcd>=400 & dlstcd<600

gen date=dlstdt
format date dlstdt %td
tempfile delist
save "`delist'", replace
* ************************************************
import sas using $Data/CRSP/dsf.sas7bdat, clear case(lower)
keep if date>=mdy(1,1,1977)
merge 1:1 permco permno date using "`delist'"
drop if _merge==2
drop _merge

replace ret =(1+ret )*(1+dlret )-1 	if date==dlstdt & dlpdt>date &  ret!=dlret  & ret +dlret <.  
replace retx=(1+retx)*(1+dlretx)-1 	if date==dlstdt & dlpdt>date & retx!=dlretx & retx+dlretx<.  

tempfile dsf
save $Temp/dsf, replace
* ************************************************
* Generate list of permco/dates from the IBES data that need return data from "`dsf'"
* $Work/XSibes.dta --> generated by read_ibes.do 
use permco permno statpers using  $Work/XSibes.dta, clear

sort statpers permno
egen time=group(statpers)
* ************************************************
* make a list of the unique statpers in IBES
preserve
keep statpers time
duplicates drop
tempfile ibes_dates
save "`ibes_dates'", replace
restore
* ************************************************
* fill in gaps in IBES coverage
tsset permno time
tsfill
replace permco=L1.permco if permco==.

* make sure that i can track an ibes observation after the last statpers for 60 statpers (i.e. 5 years)
by permno: gen last=(_n==_N)
expand 60 if last==1
sort permno time
by permno: replace     time=time[_n-1]+1 if last==1 & last==last[_n-1]
by permno: replace statpers=.            if last==1 & last==last[_n-1]
drop last

* add statpers for observations that were filled in
joinby time using "`ibes_dates'", update
drop time
gen date=statpers
format date %td

tsset permno date
tsfill
replace   permco=L1.permco 	if permco==.
replace statpers=L1.statpers 	if statpers==.

* pad the dataset through the end of 2023
gen pad=(statpers==mdy(12,14,2023))
expand 18 if pad==1
sort permno date 
by permno: replace date=date[_n-1]+1 if pad==pad[_n-1]
drop pad
* ************************************************
merge 1:1 permco permno date using $Temp/dsf
keep if _merge==3
drop _merge
* ************************************************
sort permno statpers date

* generate dividend per share & adjust for splits
by permno statpers: gen double dps=((1+ret)/(1+retx)-1)*abs(prc)
replace dps=dps/cfacpr

* cumulate dps between IBES periods:
by permno statpers: generate double DPS=(cond(missing(dps), 0, dps)) 		if _n==1
by permno statpers: replace         DPS=(cond(missing(dps), 0, dps))+DPS[_n-1]	if _n>=2

* cumulate returns between IBES periods:
by permno statpers: generate double cret =(1+cond(missing(ret ), 0, ret ))	if _n==1
by permno statpers: generate double cretx=(1+cond(missing(retx), 0, retx)) 	if _n==1

by permno statpers: replace cret =(1+cond(missing(ret ), 0, ret ))* cret[_n-1]	if _n>=2
by permno statpers: replace cretx=(1+cond(missing(retx), 0, retx))*cretx[_n-1] 	if _n>=2

* next pick the last observation for each ibes period
sort permno statpers date
collapse (lastnm) ret_mo1=cret retx_mo1=cretx DPS1=DPS (firstnm) cfacpr (count) obs=ret, by(permco permno statpers)
foreach var of varlist ret_mo1 retx_mo1 DPS1 {
	replace `var'=. if obs==0
}
drop obs
gen datem=mofd(statpers)
format datem %tm
tempfile tmp
save "`tmp'", replace
/* ********************************************************************* */
* get the risk-free rate:
import sas using $Data/CRSP/mcti.sas7bdat, case(lower) clear
gen datem=mofd(caldt)
format datem %tm
tsset datem
rename t90ret rf
keep datem rf 

tsset datem
forvalues x=1(1)60 {
	gen double   rf_mo`x'=F`x'.rf
}
merge 1:m datem using "`tmp'"
keep if _merge==3
drop _merge

tsset permno datem
forvalues x=2(1)60 {
	local j=`x'-1
	gen double  ret_mo`x'=F1.ret_mo`j'
	gen double retx_mo`x'=F1.retx_mo`j'
}
order *, seq
keep permco permno datem DPS1 ret_mo1-ret_mo60 retx_mo1-retx_mo60 rf_mo1-rf_mo60 cfacpr 
tempfile tmp
save  "`tmp'", replace
/* ********************************************************************* */
* figure out if stock pays dividends
import sas $Data/CRSP/msf.sas7bdat, clear case(lower)
keep permno permco date cfacpr cfacshr prc shrout retx ret
generate datem=mofd(date)
format date  %td
format datem %tm

replace  prc=abs(prc)
generate adj_prc=prc/cfacpr
* ************************************************
tsset permno datem
generate DP = (1+ret)/(1+retx)-1 
generate DIV=DP*L.adj_prc
forvalues x=1(1)12 {
	gen DIV`x'=F`x'.DIV
}
generate Aret=1
generate Acap=1

forvalues x=0(1)11 {
	replace Aret=Aret*(1+L`x'.ret )
	replace Acap=Acap*(1+L`x'.retx)
}	
generate ADP = Aret/Acap-1 
keep permco permno datem ADP

merge 1:1 permco permno datem using "`tmp'"
drop if _merge==1
drop _merge
save $Temp/ret_monthly_ibes, replace
* ************************************************
* add returns for ibes periods
use $Temp/ret_monthly_ibes, clear
* ***********
* add returns for calendar periods and IBES forecasts; ibes_ret.do generates $Work/ibes_ret
merge 1:1 permco permno datem using $Work/ibes_ret
* need to have IBES forecasts;  ibes_ret only has return data for permno-permco pair with the largest capitalization
keep if _merge==3
drop _merge
* ***********
* $Work/payout is generated by payout.do -- has data on expected dividends
merge 1:1 permco permno datem using $Work/payout
drop if _merge==2
drop _merge
* ***********
* get monthly data on earnings (street_mthly-- created read_compustat_qtly.do)
merge 1:1 permco permno datem using  $Work/street_mthly, keepusing(permco permno datem streetLTM)
drop if _merge==2
drop _merge
* *********
* get annual data on earnings (read_compustat-- created read_compustat.do) to fill in missing values in streetLTM
merge 1:1 permco permno datem using  $Data/read_compustat, keepusing(permco permno datem street)

*  COMPUTE EPS FOR PERMNOs (rather then PERMCOs)
tsset permno datem
bys permno: ipolate street datem, gen(streeti)
generate EPS=1000*streetLTM/(shrout*cfacshr)
replace  EPS=1000*streeti  /(shrout*cfacshr)	if EPS==.
drop streeti

tsset permno datem
gen EPS1mo=F1.EPS 
gen EPS1qr=F3.EPS 
forvalues x=1(1)10 {
	local t=12*`x'	
	gen EPS`x'Yr=F`t'.EPS 
}
* compute expected annualized growth 
gen ge1mo= 12*(ln(EPS1mo)-ln(EPS))
gen ge1qr= 04*(ln(EPS1qr)-ln(EPS))
gen ge1=(1/1)*(ln(EPS1Yr)-ln(EPS))
gen ge3=(1/3)*(ln(EPS3Yr)-ln(EPS))
gen ge5=(1/5)*(ln(EPS5Yr)-ln(EPS))
gstats winsor ge*, cuts(1 99) replace 

drop if _merge==2
drop _merge
* ***********
* median_payout comes from IBES foreacasts 
* in steady state all firms pay dividends at the same rate (i.e. the IBES sample average)
su median_payout if ADP>0 & ADP+LTG<.
scalar  paydiv=r(mean)

* "payout" is the payout ratio between t+1 and T-1
* if there is IBES data --> use it
generate payout=median_payout
su payout if payout+LTG<.

* if the firm did not pay dividends and there is no IBES data, set payout=0
replace payout=0 if ADP==0 | ADP==.

* fill-in missing values with mean expected payout ratio for firms with positive dividends
replace  payout=paydiv if payout==. & LTG<.
gen payout1=paydiv if payout==. & LTG<.
gen payout2=paydiv if payout==. & LTG+ADP<.
su payout* LTG
* ****************
generate double PVDV=.
generate double PVTV=.
generate double PRUF=.
generate double ratio=.
* *****************************************
* APPROACH #1 -- PV of earnings -- T=5
* *****************************************
su median_payout if LTG<.
scalar  paydiv=r(mean)
scalar r = 0.10716589 /* avg compounded return 1981:12-2023:12 */

local g=0.079
while `g'< r {
replace PVDV=0
forvalues yr=1(1)5 {
	local h=12*`yr'
	replace PVDV=PVDV+medest`h'/(1+r)^`yr'
}
local m=(1+`g')/(r-`g')
replace PVTV=`m'*medest`h'/(1+r)^(`h'/12)
replace PRUF=paydiv*(PVDV+PVTV) 
replace PRUF=. if PRUF<=0

replace ratio=PRUF/(prc/cfacpr)   if PRUF>0                                             
gstats winsor ratio PVTV PVDV, cuts(5 95) replace
display `g'

su ratio, d
local mean_ratio=r(mean)
display `mean_ratio'
	if (`mean_ratio')>=1.00 {
		display "Converged at g = " `g'
		continue, break
	}
local g=`g'+0.0002
}
tsset permno datem
gen double qnic1=(F1.PRUF+DPS1)/PRUF-1
gen double qcap1=(F1.PRUF+0.00)/PRUF-1
gstats winsor qnic1 qcap1, cuts(1 99) replace
su qnic*1, d
forvalues x=2(1)60 {
	local j=`x'-1
	gen qnic`x'=F1.qnic`j'
	gen qcap`x'=F1.qcap`j'
}
gen cumDiv=DPS1
forvalues x=2(1)12 {
	local j=`x'-1
	replace cumDiv=cumDiv+(cond(missing(F`j'.DPS1), 0, F`j'.DPS1))
}
gen cumqrD=DPS1
forvalues x=2(1)3 {
	local j=`x'-1
	replace cumqrD=cumqrD+(cond(missing(F`j'.DPS1), 0, F`j'.DPS1))
}
generate qnic_yr1=(F12.PRUF+cumDiv)/PRUF	
generate qnic_yr2=F12.qnic_yr1
generate qnic_yr3=F12.qnic_yr2
generate qnic_yr4=F12.qnic_yr3
generate qnic_yr5=F12.qnic_yr4
generate qnic_qr1=(F3.PRUF+cumqrD)/PRUF	
save $Temp/tmp_new, replace
* *****************************************
* APPROACH #2 -- PV of earnings -- T=10
* *****************************************
su median_payout if LTG<.
scalar  paydiv=r(mean)

forvalues x=72(12)120 {
	gen medest`x'=medest60*(1+LTG)^(`x'/12-5) if medest60>0
	
}
local g=0.063
while `g'< r {
replace PVDV=0
forvalues yr=1(1)10 {
	local h=12*`yr'
	replace PVDV=PVDV+medest`h'/(1+r)^`yr'
}
local m=(1+`g')/(r-`g')
replace PVTV=`m'*medest`h'/(1+r)^(`h'/12)
replace PRUF=paydiv*(PVDV+PVTV) 
replace PRUF=. if PRUF<=0

replace ratio=PRUF/(prc/cfacpr)   if PRUF>0                                             
gstats winsor ratio PVTV PVDV, cuts(5 95) replace
display `g'

su ratio, d
local mean_ratio=r(mean)
display `mean_ratio'
	if (`mean_ratio')>=1.00 {
		display "Converged at g = " `g'
		continue, break
	}
local g=`g'+0.00025
}
tsset permno datem
gen double qped1=(F1.PRUF+DPS1)/PRUF-1
gstats winsor qped1, cuts(1 99) replace
su qped*1, d
forvalues x=2(1)60 {
	local j=`x'-1
	gen qped`x'=F1.qped`j'
}
* *****************************************
* APPROACH #3 -- PV of earnings -- T=10 
* taper growth earnings
* *****************************************
su median_payout if LTG<.
scalar  paydiv=r(mean)
generate gw=.

local g=0.05
while `g'< r {
replace PVDV=0
forvalues yr=1(1)5 {
	local h=12*`yr'
	replace PVDV=PVDV+medest`h'/(1+r)^`yr'
}	
forvalues yr=6(1)10 {
	local h=12*`yr'
	local w=0.2*(`yr'-5)
	replace gw=(1-`w')*LTG+`w'*`g'
	replace PVDV=PVDV+medest60*(1+gw)/(1+r)^`yr'
}
local m=(1+`g')/(r-`g')
replace PVTV=`m'*medest60*(1+`g')/(1+r)^(`h'/12)
replace PRUF=paydiv*(PVDV+PVTV) 
replace PRUF=. if PRUF<=0

replace ratio=PRUF/(prc/cfacpr)   if PRUF>0                                             
gstats winsor ratio PVTV PVDV, cuts(5 95) replace
display `g'

su ratio, d
local mean_ratio=r(mean)
display `mean_ratio'
	if (`mean_ratio')>=1.00 {
		display "Converged at g = " `g'
		continue, break
	}
local g=`g'+0.005
}

tsset permno datem
gen double qslo1=(F1.PRUF+DPS1)/PRUF-1
gstats winsor qslo1, cuts(1 99) replace
su qslo*1, d
forvalues x=2(1)60 {
	local j=`x'-1
	gen qslo`x'=F1.qslo`j'
}
* *****************************************
* APPROACH #4 -- PD multiple -- T=5
* *****************************************
tsset permno datem
scalar r = 0.10716589 /* avg compounded return 1981:12-2023:12 */
local  g = 0.06675
while `g'< r {
replace PVDV=0
forvalues yr=1(1)5 {
	local h=12*`yr'
	replace PVDV=PVDV+medest`h'/(1+r)^`yr'
}
replace PVTV=medest`h'/(1+r)^(`h'/12)
local m=(1+`g')/(r-`g')
replace PRUF=payout*PVDV+paydiv*(PVTV*`m') 
replace PRUF=. if PRUF<=0

replace ratio=PRUF/(prc/cfacpr)   if PRUF>0                                             
gstats winsor ratio PVTV PVDV, cuts(5 95) replace
display `g'

su ratio
local mean_ratio=r(mean)
display `mean_ratio'
	if (`mean_ratio')>=1.00 {
		display "Converged at g = " `g'
		display "Converged at PD = " `m'
		display "Converged at PE = " `m'*paydiv
		continue, break
	}
local g=`g'+0.00025
}

tsset permno datem
gen double qruf1=(F1.PRUF+DPS1)/PRUF-1
gstats winsor qruf1, cuts(1 99) replace
forvalues x=2(1)60 {
	local j=`x'-1
	gen qruf`x'=F1.qruf`j'
}
* ***************************************
save $Temp/EBR_monthly_tmp, replace
* ***************************************
* Get returns ($Work/msf_adjusted --> generated by read_crsp.do)
use $Work/msf_adjusted, clear
keep if year(dofm(datem))>=1963

tsset permno datem
forvalues x=1(1)60 {
	gen ret`x' =F`x'.ret
	gen retx`x'=F`x'.retx
}	
gen L60ret=1
forvalues x=0(1)59 {
	gen L60ret`x'=L60ret*(1+L`x'.ret)
}	
keep permco permno datem ret* L60ret exchcd
merge 1:1 permco permno datem using $Data/read_compustat
keep if year(dofm(datem))>=1963
drop if _merge==1

keep if size+L6mcap!=.
keep if size>0 & L6mcap>0
keep if count>1
tabulate _merge

drop count _merge

* Profitability:  All NYSE, AMEX, and NASDAQ stocks for which we have market equity data for June of t, (positive) book equity data for t-1, non-missing revenues data for t-1, and non-missing data for at least one of the following: cost of goods sold, selling, general and administrative expenses, or interest expense for t-1
*INV: All NYSE, AMEX, and NASDAQ stocks for which we have market equity data for June of t and total assets data for t-2 and t-1
bys datem: astile SIZE = size 	if bookeq>0 & beme<. & ret1+size<.	, qc(exchcd==1) nq(2)
bys datem: astile SI10 = size 	if bookeq>0 & beme<. & ret1+size<.	, qc(exchcd==1) nq(10)
bys datem: astile BOOK = beme 	if bookeq>0 & beme<. & ret1+size<.	, qc(exchcd==1) nq(10)
bys datem: astile PROF = op 	if bookeq>0 & beme<. & ret1+size+op <.	, qc(exchcd==1) nq(10)
bys datem: astile INV  = inv    if bookeq>0 & beme<. & ret1+size+inv<.	, qc(exchcd==1) nq(10)

order *, seq
* ********************************
keep permco permno datem SIZE SI10 BOOK PROF INV op inv 
tsset permno datem
tsfill 
replace permco=L.permco if missing(permco)

foreach var of varlist SIZE SI10 BOOK PROF INV inv {
	replace `var'=L.`var' if `var'==. & month(dofm(datem))!=6
}	

* Fill in data for 7/2023-12/2023
expand 7 if mdy(6,1,2023)==dofm(datem), gen(exp)
sort permno datem exp
by permno: replace datem=datem[_n-1]+1 if exp==1
drop exp
* *************************
merge 1:1 permco permno datem using  $Temp/EBR_monthly_tmp
drop if _merge==1
keep if LTG<.
drop _merge
* ****************
gen STG2=medest24/medest12 		if medest24>0 & medest12>0
gen STG4=medest48/medest36 		if medest48>0 & medest36>0
gen STG6=(1+LTG)*medest60/medest60 	if medest60>0 & LTG>-1

gen FE1=ln(EPS1Yr)/1-ln(medest12)/1
gen FE3=ln(EPS3Yr)/3-ln(medest36)/3	
gen FE5=ln(EPS5Yr)/5-ln(EPS)/5-LTG
* *******************************
gen raw_FE1=EPS1Yr/medest12		if medest12>0 
gen raw_FE3=EPS3Yr/medest36		if medest36>0 
gen raw_FE5=EPS5Yr/EPS-(1+LTG)^5	if EPS>0 
* *******************************
gen FGE1=ln(medest12)/1-ln(EPS)/1
gen FGE3=ln(medest36)/3-ln(EPS)/3	
* *******************************
tsset permco datem
gen L1STG2=L12.STG2
gen L1FE1=L12.FE1
gen L3FE3=L36.FE3
gen L5FE5=L60.FE5

gen L1raw_FE1=L12.raw_FE1
gen L3raw_FE3=L36.raw_FE3
gen L5raw_FE5=L60.raw_FE5
* *******************************
gen cp1M=ln(F1.medest12)-ln(F1.EPS)-ln(medest13/medest1)
gen cp1Q=ln(F3.medest12)-ln(F3.EPS)-ln(medest15/medest3)

gen cp1=ln(F12.medest12)-ln(EPS1Yr)-ln(STG2)
gen cp3=ln(F36.medest12)-ln(EPS3Yr)-ln(STG4)
gen cp5=ln(F60.medest12)-ln(EPS5Yr)-ln(STG6)

gen d1FLTG=ln(1+F12.LTG)-ln(1+LTG)
gen d3FLTG=ln(1+F36.LTG)-ln(1+LTG)
gen d5FLTG=ln(1+F60.LTG)-ln(1+LTG)
* *******************************
gen SR2=ln(F12.STG2) 
gen SR4=ln(F12.STG4)
gen SR6=ln(F12.STG6) 
* *******************************
tsset permco datem
gen d2TGT_mo1=F1.ptg/ptg
gen dpTGT_yr1=ptgYr1/ptg
gen dpTGT_yr3=ptgYr3/ptg
gen dpTGT_yr5=ptgYr5/ptg
gstats winsor d*TGT* FGE*, cuts(1 99) replace 

* get monthly data on book (beme_mthly-- created read_compustat_qtly.do)
merge 1:1 permco permno datem using $Work/bemeLTM, keepusing(permco permno datem bookLTM bemeLTM)
drop if _merge==2
drop _merge

* get monthly data on book and earnings (street_mthly-- created read_compustat_qtly.do)
merge 1:1 permco permno datem using  $Work/street_mthly, keepusing(permco permno datem streetLTM)
drop if _merge==2
drop _merge

* Add (monthly) momentum scores (from MOME.do)
merge 1:1 permco permno datem using $Work/MOME
drop if _merge==2
drop _merge

save $Work/EBR_new_monthly, replace
* *****************************************************
